package wsi.exp.exp5;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.rygh.core.util.UIo;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlParser;
import org.rygh.core.xml.XmlTree;

import wsi.exp.ExpFolderController;
import wsi.function.Function;
import wsi.function.FunctionController;
import wsi.script.AppConfig;
import wsi.ws.WebServices;

public class Exp5Judger {

	private final Map<Function, JudgeTuple> judgeMap = new HashMap<>();

	private final File experimentDir;
	private boolean judgeAll = false;

	public Exp5Judger(File experimentDir) {
		this.experimentDir = experimentDir;

		load(AppConfig.shared().getKnowledgeDatabase());
	}

	public void judge() {
		File sdir, fdir;

		System.out.printf("[info] map size %d\n", judgeMap.size());

		for (Entry<Function, JudgeTuple> e : judgeMap.entrySet()) {
			Function function = e.getKey();

			if (!judgeAll) {
				if (function.getName().equals("getPublisherByName"))
					continue;
			}

			// if (!function.getService().equals("last_fm"))
			// continue;
			//
			// if (!function.getName().equals("getTracksByArtistName"))
			// continue;

			JudgeTuple tuple = e.getValue();

			JudgeFileManager jm = new JudgeFileManager(function);

			sdir = new File(experimentDir, function.getService());

			if (!sdir.exists())
				continue;

			assert sdir.isDirectory();

			fdir = new File(sdir, function.getName());

			if (!fdir.exists())
				continue;

			assert fdir.isDirectory() : fdir.getAbsolutePath();

			System.out.printf("[info] judge %s\n", function.getFullName());

			for (File f : jm.getXmlFiles())
				judge(f, jm.getJudgeFile(f.getName()), tuple);
		}
	}

	private void judge(File xmlFile, File judgedFile, JudgeTuple tuple) {
		String xml = UIo.readFile(xmlFile);

		XmlParser parser = new XmlParser(xml);
		XmlTree tree = parser.parse();
		assert parser.isValid();

		List<XmlLeaf> nodes = tree.getNodesByXPath("t:matched_entity");
		Set<XmlLeaf> parentSet = new HashSet<>();

		JudgeFile oldJudgeFile = new JudgeFile(judgedFile);
		JudgeFile newJudgeFile = new JudgeFile(nodes.size());

		// System.out.printf("[info] judge file %s, size %d\n",
		// xmlFile.getName(),
		// nodes.size());

		for (int i = 0; i < nodes.size(); i++) {
			if (!oldJudgeFile.isEmpty()) {
				Boolean judged = oldJudgeFile.get(i);
				newJudgeFile.set(i, judged);

				if (judged != null)
					continue;
			}

			XmlLeaf n = nodes.get(i);
			assert !n.isLeaf();

			if (parentSet.contains(n.getParent())) {
				System.out.printf(
						"[warn] duplicated appended children @ %s, %s\n",
						xmlFile.getAbsolutePath(), n.getParent().getXpath());
			}

			parentSet.add(n.getParent());

			// if (xmlFile.getName().equals("exp2b_y_George_Washington.xml")) {
			// JudgeTuple.__DEBUG = true;
			// System.out.printf("[info] judge %s\n", tuple.judge(n));
			// JudgeTuple.__DEBUG = false;
			// }

			newJudgeFile.set(i, tuple.judge(n));

		}

		assert newJudgeFile.size() == nodes.size();
		save(xmlFile, newJudgeFile);

	}

	public void judgeAll() {
		judgeAll = true;
		judge();
	}

	private void load(String knowledgeDb) {
		for (WebServices ws : WebServices.values()) {
			File gdir = ExpFolderController.shared().getGoldFolderForExp5();
			File gfile = new File(gdir, String.format("%s.xml", ws.name()));

			if (!gfile.exists()) {
				// System.out.printf("[warn] gold file not exist %s\n",
				// gfile.getName());
				continue;
			}

			String content = UIo.readFile(gfile);
			XmlParser parser = new XmlParser(content);
			XmlTree tree = parser.parse();

			if (!parser.isValid())
				continue;

			FunctionController fctrl = FunctionController.shared();
			Function function;
			JudgeTuple jtuple;
			String fname, suffix, def_str;
			boolean def;

			List<XmlLeaf> nodes = tree.getNodesByXPath("/functions/function");
			List<String> conditions = new ArrayList<>();

			for (XmlLeaf n : nodes) {
				fname = n.getValue().getAttribute("name");

				function = fctrl.getFunction(ws.name(), fname);
				jtuple = new JudgeTuple(knowledgeDb, function);

				if (!n.isLeaf())
					for (XmlLeaf g : n.getChildren()) {
						conditions.clear();
						XmlObject o = g.getValue();
						def_str = o.getAttribute("default");
						suffix = o.getAttribute("suffix");

						if (suffix != null && suffix.equals("empty"))
							suffix = null;

						def = def_str == null ? true : Boolean
								.parseBoolean(def_str);

						if (!g.isLeaf())
							for (XmlLeaf p : g.getChildren()) {
								XmlObject obj = p.getValue();

								conditions.add(obj.getAttribute("xpath"));
								conditions.add(obj
										.getAttribute("predicate_path"));
							}

						jtuple.addConditionPairs(def, suffix, conditions);
					}

				judgeMap.put(function, jtuple);
			}
		}
	}

	private void save(File file, JudgeFile newJudgeFile) {
		String fn = UIo.getFileNameWithoutExtension(file.getName());
		String fname = String.format("%s%s.txt", fn, JudgeFileManager._JUDGED);

		File jfile = new File(file.getParent(), fname);

		UIo.saveFile(jfile, newJudgeFile.toString());
	}

	public void setup() {

		Map<String, XmlTree> map = new HashMap<>();
		XmlTree tree;
		XmlLeaf root;
		XmlObject obj;

		for (Function f : FunctionController.shared().getAllFunctions()) {
			if (!map.containsKey(f.getService())) {
				tree = new XmlTree();
				map.put(f.getService(), tree);

				obj = new XmlObject("functions");
				root = new XmlLeaf(tree.getRoot(), obj);
			} else {
				tree = map.get(f.getService());
				root = tree.getConcreteRoot();
			}

			obj = new XmlObject("function");
			obj.addAttribute("name", f.getName());
			new XmlLeaf(root, obj);
		}

		for (Entry<String, XmlTree> e : map.entrySet()) {
			File file = new File("/home/nanmus/Downloads", e.getKey() + ".xml");
			UIo.saveFile(file, e.getValue().toXmlString());
		}
	}
}
